package com.ytjj.qmyx.mall.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.v3.util.PemUtils;
import com.github.binarywang.wxpay.v3.util.SignUtils;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ytjj.common.constants.CopyWriterConstant;
import com.ytjj.common.exception.ApiException;
import com.ytjj.common.utils.CopyWriterConfigUtil;
import com.ytjj.qmyx.mall.config.MyWxConfig;
import com.ytjj.qmyx.mall.constants.CompanyConstant;
import com.ytjj.qmyx.mall.mapper.OrdersDao;
import com.ytjj.qmyx.mall.mapper.OrdersPayMapper;
import com.ytjj.qmyx.mall.mapper.PayConfigMapper;
import com.ytjj.qmyx.mall.model.OrdersPay;
import com.ytjj.qmyx.mall.model.OrdersPayExample;
import com.ytjj.qmyx.mall.model.PayConfig;
import com.ytjj.qmyx.mall.model.dto.WxAppPayDto;
import com.ytjj.qmyx.mall.model.dto.WxPayDto;
import com.ytjj.qmyx.mall.service.WxService;
import com.ytjj.qmyx.mall.utils.WxPayServiceUtil;
import com.ytjj.qmyx.mall.utils.WxpayV3Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信小程序发起支付
 *
 *
 * @version 1.0
 *
 */
@Service("APP-JSAPI")
@Slf4j
public class WxAppJsApiServiceImpl implements WxService<WxPayDto>{

    @Resource
    private OrdersDao ordersDao;
    @Resource
    private CopyWriterConfigUtil copyWriterConfigUtil;
    @Resource
    private OrdersPayMapper ordersPayMapper;
    @Resource
    private PayConfigMapper payConfigMapper;


    public static final String TYPE = "APP";

    public static final String TRADE_TYPE = "JSAPI";
    public static final String JSAPI_PHONE_CHARGE = "JSAPI_PHONE_CHARGE";

    /**
     * 支付方式：v2/v3
     */
    @Value("${payment.type:v3}")
    private String paymentType;

    @Override
    public Map<String, String> doUnifiedOrder(WxPayDto wxAppPayDto) throws Exception {
        PayConfig payConfig = ordersDao.getPayConfigByCompany(CompanyConstant.PLATFORM, TYPE, TRADE_TYPE);
        if("v3".equals(paymentType)){
            // 构造请求数据
            JSONObject reqJSON = WxPayServiceUtil.buildV3OrderRequest(wxAppPayDto, payConfig);
            JSONObject payer = new JSONObject();
            payer.put("openid", wxAppPayDto.getOpenid());
            reqJSON.put("payer", payer);

            WxPayService wxPayService= WxPayServiceUtil.getWxPayService(payConfig);
            wxPayService.getConfig().setTradeType(WxPayConstants.TradeType.JSAPI);
            wxPayService.getConfig().setPrivateKeyPath(payConfig.getPrivateKeyPath());
            wxPayService.getConfig().setCertSerialNo(payConfig.getCertSerialNo());
            wxPayService.getConfig().setApiV3Key(payConfig.getWxKey());
            String reqUrl = WxpayV3Util.NORMALMCH_URL_MAP.get(WxPayConstants.TradeType.JSAPI);
            try {
                // 调起上游接口：
                // 1. 如果抛异常，则订单状态为： 生成状态，此时没有查单处理操作。 订单将超时关闭
                // 2. 接口调用成功， 后续异常需进行捕捉， 如果 逻辑代码出现异常则需要走完正常流程，此时订单状态为： 支付中， 需要查单处理。
                log.info("微信请求 {}",JSONObject.toJSONString(reqJSON));
                JSONObject resJSON = WxpayV3Util.unifiedOrderV3(reqUrl, reqJSON, wxPayService.getConfig());
                log.info("微信返回 {}",resJSON);
                Map<String, String> payInfo = new HashMap<>(); // 如果用JsonObject会出现签名错误
                String packageStr = ""+ resJSON.get("package");
                String timestamp = System.currentTimeMillis() / 1000 + "";
                String nonceStr = WXPayUtil.generateNonceStr();
                payInfo.put("appId", payConfig.getAppid());
                payInfo.put("timeStamp", timestamp);
                payInfo.put("nonceStr", nonceStr);
                payInfo.put("package", packageStr);
                payInfo.put("signType", "RSA");

                String beforeSign = String.format("%s\n%s\n%s\n%s\n", payConfig.getAppid(), timestamp, nonceStr, packageStr);
                payInfo.put("sign", SignUtils.sign(beforeSign, PemUtils.loadPrivateKey(new FileInputStream(wxPayService.getConfig().getPrivateKeyPath()))));

                payInfo.put("payConfigId", String.valueOf(payConfig.getId()));
                payInfo.put("redirect_url", payConfig.getRedirectUrl());
                return payInfo;
            }catch (Exception e) {
                log.error("===调用微信支付接口报错,{}",e);
                throw new Exception(e.getMessage());
            }
        }



        MyWxConfig myWxConfig = new MyWxConfig(payConfig);
        Map<String, String> data = new HashMap<>();
        data.put("appid", myWxConfig.getAppID());
        data.put("attach", "");
        data.put("body", wxAppPayDto.getBody());
        data.put("mch_id", myWxConfig.getMchID());
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("notify_url", payConfig.getNotifyUrl());
        data.put("openid", wxAppPayDto.getOpenid());
        data.put("out_trade_no", wxAppPayDto.getResOrderNo());
        data.put("spbill_create_ip", payConfig.getSpbillCreateIp());
        data.put("total_fee", wxAppPayDto.getRealTotalMoney());
        data.put("trade_type", TRADE_TYPE);

        //优惠功能
//        if (wxAppPayDto.getProductId()!=null){
//            JSONObject detail = new JSONObject();
//            JSONArray goods_detail = new JSONArray();
//            JSONObject goodsInfo = new JSONObject();
//            goodsInfo.put("goods_id",wxAppPayDto.getProductId().toString());
//            goodsInfo.put("quantity",1);
//            goodsInfo.put("price",Integer.parseInt(wxAppPayDto.getRealTotalMoney()));
//            goods_detail.add(goodsInfo);
//            detail.put("goods_detail",goods_detail);
//            data.put("detail", detail.toJSONString());
//            data.put("version","1.0");
//        }

        WXPay wxpay = new WXPay(myWxConfig, WXPayConstants.SignType.MD5);
        data.put("sign", WXPayUtil.generateSignature(data, payConfig.getWxKey(),
                WXPayConstants.SignType.MD5));
        log.info("APPJSAPIDATA参数,{}", JSON.toJSONString(data));
        Map<String, String> response = wxpay.unifiedOrder(data);
        log.info("response参数,{}",response);
        if ("SUCCESS".equals(response.get("return_code"))) {
            Map<String, String> param = new HashMap<>();
            param.put("appId", payConfig.getAppid());
            param.put("nonceStr", WXPayUtil.generateNonceStr());
            param.put("package", "prepay_id=" + response.get("prepay_id"));
            param.put("signType", "MD5");
            param.put("timeStamp", System.currentTimeMillis() / 1000 + "");
            param.put("paySign", WXPayUtil.generateSignature(param, payConfig.getWxKey(),
                    WXPayConstants.SignType.MD5));

            param.put("payConfigId", String.valueOf(payConfig.getId()));
            return param;
        }
        throw new ApiException(copyWriterConfigUtil.getCopyWriterValue(CopyWriterConstant.ORDER_FAILED));
    }

    @Override
    public Map<String, String> doUnifiedRechargeOrder(WxPayDto wxAppPayDto) throws Exception {
        PayConfig payConfig = ordersDao.getPayConfigByCompany(CompanyConstant.PLATFORM, TYPE, JSAPI_PHONE_CHARGE);
        MyWxConfig myWxConfig = new MyWxConfig(payConfig);

        Map<String, String> data = new HashMap<>();
        data.put("appid", myWxConfig.getAppID());
        data.put("attach", "");
        data.put("body", wxAppPayDto.getBody());
        data.put("mch_id", myWxConfig.getMchID());
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("notify_url", payConfig.getNotifyUrl());
        data.put("openid", wxAppPayDto.getOpenid());
        data.put("out_trade_no", wxAppPayDto.getResOrderNo());
        data.put("spbill_create_ip", payConfig.getSpbillCreateIp());
        data.put("total_fee", wxAppPayDto.getRealTotalMoney());
        data.put("trade_type", TRADE_TYPE);

        WXPay wxpay = new WXPay(myWxConfig, WXPayConstants.SignType.MD5);
        data.put("sign", WXPayUtil.generateSignature(data, payConfig.getWxKey(),
                WXPayConstants.SignType.MD5));

        Map<String, String> response = wxpay.unifiedOrder(data);
        if ("SUCCESS".equals(response.get("return_code"))) {
            Map<String, String> param = new HashMap<>();
            param.put("appId", payConfig.getAppid());
            param.put("nonceStr", WXPayUtil.generateNonceStr());
            param.put("package", "prepay_id=" + response.get("prepay_id"));
            param.put("signType", "MD5");
            param.put("timeStamp", System.currentTimeMillis() / 1000 + "");
            param.put("sign", WXPayUtil.generateSignature(param, payConfig.getWxKey(),
                    WXPayConstants.SignType.MD5));
            return param;
        }
        throw new ApiException(copyWriterConfigUtil.getCopyWriterValue(CopyWriterConstant.ORDER_FAILED));
    }

    @Override
    public Map<String, String> wxOrderQuery(String resOrderNo) throws Exception {
        PayConfig payConfig = ordersDao.getPayConfigByCompany(CompanyConstant.PLATFORM, TYPE, TRADE_TYPE);
        MyWxConfig myWxConfig = new MyWxConfig(payConfig);

        Map<String, String> data = new HashMap<>();
        data.put("appid", payConfig.getAppid());
        WXPay wxpay = new WXPay(myWxConfig, WXPayConstants.SignType.MD5);

        data.put("mch_id", payConfig.getMchId());
        data.put("out_trade_no", resOrderNo);
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("sign", WXPayUtil.generateSignature(data, payConfig.getWxKey(),
                WXPayConstants.SignType.MD5));
        log.info("微信小程序支付状态查询请求 订单号：{},query: {}", resOrderNo, JSONObject.toJSONString(data));
        //使用官方API请求预付订单
        Map<String, String> response = wxpay.orderQuery(data);
        log.info("微信小程序支付状态查询响应 订单号：{},response: {}", resOrderNo, JSONObject.toJSONString(response));
        return response;
    }

    @Override
    public Map<String, String> wxPhoneChargesQuery(String resOrderNo) {
        return null;
    }

    @Override
    public Map refundOrders(String outTradeNo, String outRefundNo, Integer totalFee, Integer refundFee, String refundDesc) throws Exception {
//        PayConfig payConfig = ordersDao.getPayConfigByCompany(CompanyConstant.PLATFORM, TYPE, TRADE_TYPE);
        OrdersPayExample ordersPayExample = new OrdersPayExample();
        ordersPayExample.createCriteria().andResOrderNoEqualTo(outTradeNo).andPayTypeEqualTo(new Byte("2"));
        ordersPayExample.setOrderByClause("id desc");
        List<OrdersPay> ordersPays = ordersPayMapper.selectByExample(ordersPayExample);
        PayConfig payConfig = null;
        if (!CollectionUtils.isEmpty(ordersPays)) {
            payConfig = payConfigMapper.selectByPrimaryKey(ordersPays.get(0).getPayConfigId());
        }
        if (null == payConfig) {
            throw new ApiException("退款失败，请联系管理员！！！");
        }

        MyWxConfig myWxConfig = new MyWxConfig(payConfig);

        Map<String, String> data = new HashMap<>();
        //生成商户订单号，不可重复
        WXPay wxpay = new WXPay(myWxConfig, WXPayConstants.SignType.MD5);
        data.put("appid", payConfig.getAppid());
        data.put("mch_id", payConfig.getMchId());
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("out_trade_no", outTradeNo);
        data.put("out_refund_no", outRefundNo);
        data.put("total_fee", String.valueOf(totalFee));
        data.put("refund_fee", String.valueOf(refundFee));
//        data.put("refund_desc", refundDesc);
        data.put("notify_url", payConfig.getRefundNotifyUrl());
        data.put("sign", WXPayUtil.generateSignature(data, payConfig.getWxKey(),
                WXPayConstants.SignType.MD5));
        Map<String, String> response = wxpay.refund(data);
        log.info("微信发起退款：response:{}",JSONObject.toJSONString(response));
        return response;
    }

    @Override
    public Map refundRecharge(String outTradeNo, String outRefundNo, Integer totalFee, Integer refundFee, String refundDesc) throws Exception {
        PayConfig payConfig = ordersDao.getPayConfigByCompany(CompanyConstant.PLATFORM, TYPE, JSAPI_PHONE_CHARGE);
        MyWxConfig myWxConfig = new MyWxConfig(payConfig);

        Map<String, String> data = new HashMap<>();
        //生成商户订单号，不可重复
        WXPay wxpay = new WXPay(myWxConfig, WXPayConstants.SignType.MD5);
        data.put("appid", payConfig.getAppid());
        data.put("mch_id", payConfig.getMchId());
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        data.put("out_trade_no", outTradeNo);
        data.put("out_refund_no", outRefundNo);
        data.put("total_fee", String.valueOf(totalFee));
        data.put("refund_fee", String.valueOf(refundFee));
//        data.put("refund_desc", refundDesc);
        data.put("notify_url", payConfig.getRefundNotifyUrl());
        data.put("sign", WXPayUtil.generateSignature(data, payConfig.getWxKey(),
                WXPayConstants.SignType.MD5));
        Map<String, String> response = wxpay.refund(data);
        log.info("微信发起退款：response:{}",JSONObject.toJSONString(response));
        return response;
    }
}
